﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ATMS.DAL;

namespace ATMS.Repository
{
    /// <summary>
    /// Generic implementation of the Unit of Work pattern
    /// </summary>
    public class UnitOfWork : IUnitOfWork
    {
        private readonly IDbContext _context;

        private bool _disposed;
        private Hashtable _repositories;

        public UnitOfWork(IDbContext context)
        {
            _context = context;
        }


        // Some DI/IoC containers support multiple constructors.  Since the SimpleInjector IoC only uses one constructor, omitting this one.
        //public UnitOfWork()
        //{
        //    _context = new AtmsContext();
        //}


        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        public void Save()
        {
            _context.SaveChanges();
        }

        public void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    _context.Dispose();
                }
            }
            _disposed = true;
        }

        /// <summary>
        /// Creates repository instances on demand
        /// Storing all the activated instances of repositories for each and every request
        /// Given a request for a repository, create new Hashtable if none exists
        /// If the requested repository has been created, return it; if not, activate the requested repository instnace, store it in the Hashtable and return it
        /// Return the repository abstraction, IRepository, not the concrete implementation
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public IRepository<T> Repository<T>() where T : class
        {
            if (_repositories == null)
            {
                _repositories = new Hashtable();
            }
            var type = typeof (T).Name;

            if (!_repositories.ContainsKey(type))
            {
                var repositoryType = typeof (Repository<>);

                // if the Repository class is missing a default constructor, an somewhat confusing exception occurs.  For example, if we are using the USER entity:
                //    - MethodMissingException Constructor on type 'ATMS.Repository.IRepository`1[[ATMS.DAL.USER, ATMS.DAL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' not found.
                // This error is actually referring to the generic repository class, and not the referenced entity -
                // in this case, 'Repository' used in "... typeof (Repository<>)" above.
                
                // Makes call to  ATMS.Repository default constructor - public Repository(IDbContext context) {}
                var repositoryInstance = Activator.CreateInstance(repositoryType.MakeGenericType(typeof(T)),
                                                                  new object[] { _context });

                _repositories.Add(type, repositoryInstance);
            }

            return (IRepository<T>) _repositories[type];
        }
    }
}
